package cn.sunxiansheng.log4j2.aspectj;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;

/**
 * 日志切面
 *
 * @author sunxiansheng
 */
@Aspect
@Slf4j
public class LogAspect {

    // 使用 Gson 序列化对象，启用 PrettyPrinting，输出格式化的 JSON
    private static final Gson GSON = new GsonBuilder()
            .setPrettyPrinting() // 启用格式化输出
            .disableHtmlEscaping() // 禁用 HTML 转义，保留字符原始形式
            .create();

    // ANSI 颜色代码
    private static final String ANSI_RESET = "\u001B[0m";     // 重置颜色
    private static final String ANSI_CYAN = "\u001B[92m";     // 绿色

    /**
     * 配置切点，匹配所有 controller 和 service 包中的所有方法
     */
    @Pointcut("execution(public * *..controller..*(..)) || " +
            "execution(public * *..service..*(..))")
    public void applicationPackagePointcut() {
        // 方法为空，这是一个切点定义
    }

    /**
     * 环绕通知，记录方法执行的详细信息
     *
     * @param joinPoint 切入点
     * @return 方法执行结果
     * @throws Throwable 异常
     */
    @Around("applicationPackagePointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法签名和详细信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String className = signature.getDeclaringTypeName();
        String methodName = signature.getName();

        // 获取方法参数
        Object[] args = joinPoint.getArgs();
        String requestParams = GSON.toJson(args);

        // 记录方法进入日志，参数从下一行开始输出，参数部分以黄色显示
        log.info("==> 进入方法: {}.{}()", className, methodName);
        log.info("参数:\n" + ANSI_CYAN + "{}" + ANSI_RESET, requestParams);

        // 记录方法执行开始时间
        long startTime = System.currentTimeMillis();

        // 执行目标方法
        Object result = joinPoint.proceed();

        // 记录方法执行结束时间
        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;

        // 获取方法返回值
        String response = GSON.toJson(result);

        // 记录方法退出日志，返回值从下一行开始输出，返回值部分以黄色显示
        log.info("<== 退出方法: {}.{}() | 耗时: {} ms", className, methodName, executionTime);
        log.info("返回值:\n" + ANSI_CYAN + "{}" + ANSI_RESET, response);

        return result;
    }
}